package it.niedermann.android.markdown.markwon

import android.annotation.SuppressLint
import android.content.Context
import android.os.Build
import android.text.TextWatcher
import android.util.AttributeSet
import android.util.Log
import androidx.annotation.ColorInt
import androidx.appcompat.widget.AppCompatEditText
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import io.noties.markwon.base.Markwon
import io.noties.markwon.base.editor.MarkwonEditor
import io.noties.markwon.base.editor.MarkwonEditor.Companion.builder
import io.noties.markwon.base.editor.handler.EmphasisEditHandler
import io.noties.markwon.base.editor.handler.StrongEmphasisEditHandler
import it.niedermann.android.markdown.MarkdownEditor
import it.niedermann.android.markdown.markwon.format.ContextBasedFormattingCallback
import it.niedermann.android.markdown.markwon.format.ContextBasedRangeFormattingCallback
import it.niedermann.android.markdown.markwon.handler.BlockQuoteEditHandler
import it.niedermann.android.markdown.markwon.handler.CodeBlockEditHandler
import it.niedermann.android.markdown.markwon.handler.CodeEditHandler
import it.niedermann.android.markdown.markwon.handler.HeadingEditHandler
import it.niedermann.android.markdown.markwon.handler.StrikethroughEditHandler
import it.niedermann.android.markdown.markwon.textwatcher.CombinedTextWatcher
import it.niedermann.android.markdown.markwon.textwatcher.SearchHighlightTextWatcher
import timber.log.Timber
import java.util.function.Consumer

open class MarkwonMarkdownEditor @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = android.R.attr.editTextStyle
) : AppCompatEditText(context, attrs, defStyleAttr), MarkdownEditor {
    private var listener: Consumer<CharSequence>? = null
    private val `unrenderedText$` = MutableLiveData<CharSequence>()
    private var combinedWatcher: CombinedTextWatcher? = null
    var markwonEditor: Markwon? = null

    /*public void init( MarkwonEditor inEditor,MarkwonTheme theme){
        var editor= inEditor;
        if(editor == null){
            var markwon = createMarkwonBuilder(getContext(),theme).build();
            editor = createMarkwonEditorBuilder(markwon).build();
        }

        combinedWatcher = new CombinedTextWatcher(editor, this);

        initListener();
    }*/
    fun init(markwonEditor: Markwon) {
        this.markwonEditor = markwonEditor
        val editor = createMarkwonEditorBuilder(markwonEditor).build()
        combinedWatcher = CombinedTextWatcher(editor, this)
        initListener()
    }

    private fun initListener() {
        addTextChangedListener(combinedWatcher)
        customSelectionActionModeCallback =
            ContextBasedRangeFormattingCallback(this)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            customInsertionActionModeCallback = ContextBasedFormattingCallback(this)
        }
    }

    override fun setSearchColor(@ColorInt color: Int) {
        val searchHighlightTextWatcher = combinedWatcher!!.get<SearchHighlightTextWatcher>(
            SearchHighlightTextWatcher::class.java
        )
        searchHighlightTextWatcher?.setSearchColor(color)
            ?: Timber.tag(TAG).w( " is not a registered ")
    }

    @SuppressLint("BinaryOperationInTimber")
    override fun setSearchText(searchText: CharSequence, current: Int?) {
        val searchHighlightTextWatcher = combinedWatcher!!.get<SearchHighlightTextWatcher>(
            SearchHighlightTextWatcher::class.java
        )
        searchHighlightTextWatcher?.setSearchText(searchText, current)
            ?: Timber.tag(TAG)
                .w(SearchHighlightTextWatcher::class.java.simpleName + " is not a registered " + TextWatcher::class.java.simpleName)
    }

    override fun setMarkdownString(text: CharSequence) {
        setText(text)
        setMarkdownStringModel(text)
    }

    override fun setMarkdownString(text: CharSequence, afterRender: Runnable?) {
        throw UnsupportedOperationException("This is not available in " + MarkwonMarkdownEditor::class.java.simpleName + " because the text is getting rendered all the time.")
    }

    /**
     * Updates the current model which matches the rendered state of the editor *without* triggering
     * anything of the native [EditText]
     */
    fun setMarkdownStringModel(text: CharSequence) {
        `unrenderedText$`.value = text.toString() ?: ""
        if (listener != null) {
            listener!!.accept(text)
        }
    }

    override fun getMarkdownString(): LiveData<CharSequence> {
        return `unrenderedText$`
    }

    override fun setMarkdownStringChangedListener(listener: Consumer<CharSequence>?) {
        this.listener = listener
    }

    companion object {
        private val TAG = MarkwonMarkdownEditor::class.java.simpleName
        private fun createMarkwonEditorBuilder(markwon: Markwon): MarkwonEditor.Builder {
            return builder(markwon)
                .useEditHandler(EmphasisEditHandler())
                .useEditHandler(StrongEmphasisEditHandler())
                .useEditHandler(StrikethroughEditHandler())
                .useEditHandler(CodeEditHandler())
                .useEditHandler(CodeBlockEditHandler())
                .useEditHandler(BlockQuoteEditHandler())
                .useEditHandler(HeadingEditHandler())
        }
    }
}